#pragma once

#include <carbon/defs.h>
#include <memory>
#include <sstream>
#include <string>
#include <cstring>
#include <errno.h>

CARBON_NS_BEGIN

namespace errors {
    static const int eSuccess            =  0;      // success
    static const int eFail               =  1;      // fail
    static const int eSystem             =  2;      // system call error
    static const int eTimeout            =  3;      // timeout
    static const int eInvalidArgument    =  4;      // invalid arguments
} // namespace errors

/// @brief 错误类。
class Error : private Noncopyable {
public:
    struct Cause {
        int         code;
        String      msg;
        String      pos;
        std::unique_ptr<Cause> cause;
    };

public:
    Error()  = default;
    Error(const char * file, int line, int code, std::string && msg)  { this->push(file, line, code, std::move(msg)); }

    Error(Error && other) noexcept { cause_ = std::move(other.cause_); }

    Error & operator=(Error && other) noexcept;

    operator bool() const { return cause_ != nullptr; }

    Error & push(const char * file, int line, int code, std::string && msg);

    int code() const { if ( cause_ ) return cause_->code; else return 0; }

    String message() const { if ( cause_ ) return cause_->msg; else return String(); }

    String position() const { if ( cause_ ) return cause_->pos; else return String(); }

    const Cause * cause() const { return cause_.get(); }

    String toString() const;

    friend std::ostream & operator<<(std::ostream & os, const Error & error);

private:
    std::unique_ptr<Cause> cause_;
}; // class Error

std::string buildSystemErrorMesage(int error_code, const char * prefix);

CARBON_NS_END

#define CARBON_NO_ERROR() CRA_NS::Error()
#define CARBON_MAKE_ERROR(code, msg) std::move(CRANE_NS::Error(__FILE__, __LINE__, (int)code, std::move(msg)))
#define CARBON_PUSH_ERROR(err, code, msg) err.push(__FILE__, __LINE__, code, std::move(msg))
#define CARBON_MAKE_SYSTEM_ERROR(code, prefix) std::move(CRA_NS::Error(__FILE__, __LINE__, code, carbon::buildSystemErrorMesage((int)crane::ErrorCode::eSystem, prefix)))

